﻿using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using LFSRecord2.Events;
using LFSRecord2.Model;
using LFSRecord2.Model.UserActions;

namespace LFSRecord2.View.TimelineStructures
{
    /// <summary>
    /// Interaction logic for PropertyValueBindingControl.xaml
    /// </summary>
    public partial class PropertyValueBindingControl : Grid
    {
        int NumLayers;
        LayerInfoObject[] LayersInfo;

        public PropertyValueBindingControl()
        {
            InitializeComponent();
        }

        private void Grid_Loaded(object sender, RoutedEventArgs e)
        {
            ((CompLayerPropertyBase)DataContext).PropertyChanged += PropertyValueBindingControl_PropertyChanged;
            SetToolTip();
        }

        void PropertyValueBindingControl_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            SetToolTip();
        }

        private void SetToolTip()
        {
            CompLayerPropertyBase prop = (CompLayerPropertyBase)DataContext;
            if (prop.HasBoundProperty)
            {
                ToolTip = "Bound to property '" + prop.BoundProperty.PropertyName + "' on layer '" + prop.BoundProperty.ParentLayer.LayerName + "'";
            }
            else
            {
                ToolTip = "No Property Binding";
            }
        }

        private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;

            //Debug.WriteLine("binding icon clicked");
            NumLayers = LfsRecordController.project.Layers.Count;
            LayersInfo = new LayerInfoObject[NumLayers];

            // Populate layersInfo
            for (int a = 0; a < NumLayers; a++)
            {
                LayersInfo[a] = new LayerInfoObject(a, -1, LfsRecordController.project.Layers[a].LayerType, LfsRecordController.project.Layers[a].LayerName, LfsRecordController.project.Layers[a].Properties.Count, -1);

                for (int b = 0; b < LfsRecordController.project.Layers[a].Properties.Count; b++)
                {
                    LayersInfo[a].Properties[b] = new PropertyInfoObject(b, LfsRecordController.project.Layers[a].Properties[b].PropertyValueType, LfsRecordController.project.Layers[a].Properties[b].PropertyName);
                }
            }

            // Build and show selection context menu
            ContextMenu = null;
            ShowContextMenu();
        }

        private void ShowContextMenu()
        {
            CompLayerPropertyBase propSelf = (CompLayerPropertyBase)DataContext;

            // Create context menu
            MenuItem mi, mi2;
            Binding myBinding;
            ContextMenu cm = new ContextMenu();

            for (int a = 0; a < NumLayers; a++)
            {
                mi = new MenuItem();
                mi.Header = LayersInfo[a].LayerName;
                if (propSelf.BindingPropertyIndex.LayerIndex == a)
                    mi.IsChecked = true;

                for (int b = 0; b < LayersInfo[a].NumProperties; b++)
                {
                    mi2 = new MenuItem();
                    mi2.Header = LayersInfo[a].Properties[b].PropertyName;

                    if ((a == propSelf.ParentLayer.LayerIndex && b == propSelf.PropertyIndex) ||
                        LayersInfo[a].Properties[b].PropertyType != propSelf.PropertyValueType)
                    {
                        mi2.IsEnabled = false;
                    }
                    else
                    {
                        if (propSelf.BindingPropertyIndex.LayerIndex == a && propSelf.BindingPropertyIndex.PropertyIndex == b)
                            mi2.IsChecked = true;
                        mi2.Command = ValueBinding_ClickedCommand;
                        mi2.CommandParameter = new LayerPropIndex { LayerIndex = (short)a, PropertyIndex = (sbyte)b };
                    }
                    mi.Items.Add(mi2);
                }
                cm.Items.Add(mi);
            }

            if (propSelf.PropertyName == "Look At")
            {
                cm.Items.Add(new Separator());

                mi = new MenuItem();
                mi.Header = "Ignore parenting";
                myBinding = new Binding("BoundPropertyIgnoreParent");
                myBinding.Mode = BindingMode.OneWay;
                myBinding.Source = propSelf;
                mi.SetBinding(MenuItem.IsCheckedProperty, myBinding);
                mi.Command = IgnoreParenting_ClickedCommand;
                mi.CommandParameter = null;
                cm.Items.Add(mi);
            }

            cm.Items.Add(new Separator());

            mi = new MenuItem();
            mi.Header = "No binding";
            mi.Command = ValueBinding_ClickedCommand;
            mi.CommandParameter = null;
            cm.Items.Add(mi);

            ContextMenu = cm;
            ContextMenu.Unloaded += new RoutedEventHandler(ContextMenu_Unloaded);
            ContextMenu.IsOpen = true;
        }

        // ValueBinding_ClickedCommand
        RelayCommand _valueBinding_ClickedCommand;
        public ICommand ValueBinding_ClickedCommand
        {
            get
            {
                if (_valueBinding_ClickedCommand == null)
                    _valueBinding_ClickedCommand = new RelayCommand(param => this.ValueBinding_ClickedCommandExecute(param), param => ValueBinding_ClickedCommandCanExecute(param));
                return _valueBinding_ClickedCommand;
            }
        }
        void ValueBinding_ClickedCommandExecute(object param)
        {
            CompLayerPropertyBase propSelf = (CompLayerPropertyBase)DataContext;
            LayerPropIndex oldBinding = propSelf.BindingPropertyIndex;

            if (param == null)
            {
                //Debug.WriteLine("We have to remove the binding at property {0} on layer {1}", propSelf.BindingPropertyIndex.PropertyIndex, propSelf.BindingPropertyIndex.LayerIndex);
                propSelf.BoundProperty = null;
                ToolTip = "No Property Binding";
            }
            else
            {
                propSelf.BoundProperty = LfsRecordController.project.GetProperty((LayerPropIndex)param);
            }

            propSelf.ParentLayer.SetCurrentTransformMatrix();
            propSelf.ParentLayer.UpdatedCurrentValues = CompLayerPropertyUpdateTypes.All;
            propSelf.ParentLayer.DoneUpdatingCurrentValues = !propSelf.ParentLayer.DoneUpdatingCurrentValues;

            LayerPropIndex newBinding = propSelf.BindingPropertyIndex;
            if (oldBinding.LayerIndex != newBinding.LayerIndex || oldBinding.PropertyIndex != newBinding.PropertyIndex)
            {
                LfsRecordController.RecordUserAction(new UserActionPropertyBindingChanged()
                    {
                        LayerIndex = propSelf.ParentLayer.LayerIndex,
                        PropertyIndex = propSelf.PropertyIndex,
                        oldBinding = oldBinding,
                        newBinding = newBinding
                    });
            }
            LfsRecordController.CreateUndoLevel();

            ServicesFactory.EventService.GetEvent<CompLayerPropertyChangedEvent>().Publish(new CompLayerPropertyChangedEventArgs { Sender = this, PropertyType = "Value", UpdateCurrentValues = true });
        }
        bool ValueBinding_ClickedCommandCanExecute(object param)
        {
            CompLayerPropertyBase propSelf = (CompLayerPropertyBase)DataContext;
            if (param == null)
            {
                return propSelf.HasBoundProperty;
            }
            return true;
        }

        // IgnoreParenting_ClickedCommand
        RelayCommand _ignoreParenting_ClickedCommand;
        public ICommand IgnoreParenting_ClickedCommand
        {
            get
            {
                if (_ignoreParenting_ClickedCommand == null)
                    _ignoreParenting_ClickedCommand = new RelayCommand(param => this.IgnoreParenting_ClickedCommandExecute());
                return _ignoreParenting_ClickedCommand;
            }
        }
        void IgnoreParenting_ClickedCommandExecute()
        {
            CompLayerPropertyBase propSelf = (CompLayerPropertyBase)DataContext;

            propSelf.BoundPropertyIgnoreParent = !propSelf.BoundPropertyIgnoreParent;
            propSelf.ParentLayer.SetCurrentTransformMatrix();
            propSelf.ParentLayer.UpdatedCurrentValues = CompLayerPropertyUpdateTypes.All;
            propSelf.ParentLayer.DoneUpdatingCurrentValues = !propSelf.ParentLayer.DoneUpdatingCurrentValues;

            LfsRecordController.RecordUserAction(new UserActionPropertyBindingIgnoreParentingChanged()
                {
                    LayerIndex = propSelf.ParentLayer.LayerIndex,
                    PropertyIndex = propSelf.PropertyIndex,
                    Ignore = propSelf.BoundPropertyIgnoreParent
                });
            LfsRecordController.CreateUndoLevel();
            ServicesFactory.EventService.GetEvent<CompLayerPropertyChangedEvent>().Publish(new CompLayerPropertyChangedEventArgs { Sender = this, PropertyType = "Value", UpdateCurrentValues = true });
        }

        void ContextMenu_Unloaded(object sender, RoutedEventArgs e)
        {
            //Debug.WriteLine("Context menu gone");
            ContextMenu = null;
        }

        private void Grid_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            e.Handled = true;
        }
    }

    struct LayerInfoObject
    {
        public int LayerIndex;
        public int ParentIndex;
        public int ParentLayerIndex;
        public CompLayerTypes LayerType;
        public string LayerName;
        public int NumProperties;
        public PropertyInfoObject[] Properties;

        public LayerInfoObject(int layerIndex, int parentIndex, CompLayerTypes layerType, string layerName, int numProperties, int parentLayerIndex)
        {
            LayerIndex = layerIndex;
            ParentIndex = parentIndex;
            ParentLayerIndex = parentLayerIndex;
            LayerType = layerType;
            LayerName = layerName;
            NumProperties = numProperties;
            Properties = new PropertyInfoObject[NumProperties];
        }
    }

    struct PropertyInfoObject
    {
        public int PropertyIndex;
        public LayerPropertyValueTypes PropertyType;
        public string PropertyName;
        public LayerPropIndex BindingPropIndex;

        public PropertyInfoObject(int propertyIndex, LayerPropertyValueTypes propertyType, string propertyName)
        {
            PropertyIndex = propertyIndex;
            PropertyType = propertyType;
            PropertyName = propertyName;

            BindingPropIndex = new LayerPropIndex();
        }
    }

}
